import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import socket
import threading
import time

class SocketClientApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Socket客户端")
        self.root.geometry("800x800")
        self.root.minsize(750, 750)
        
        # 客户端状态
        self.connected = False
        self.client_socket = None
        
        # 创建界面
        self.create_widgets()
        
        # 启动时自动测试连接
        self.root.after(1000, self.auto_test)
        
    def auto_test(self):
        """自动测试连接状态"""
        self.log_message("客户端程序已启动")
        self.log_message("请确保服务器程序正在运行并点击'连接服务器'")
        
    def create_widgets(self):
        # 创建主框架
        main_paned = tk.PanedWindow(self.root, orient=tk.VERTICAL, sashrelief=tk.RAISED, sashwidth=4)
        main_paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 上半部分：配置和发送区域
        top_frame = ttk.Frame(main_paned, padding=10)
        main_paned.add(top_frame, height=350)  # 增加高度以容纳发送按钮
        
        # 下半部分：消息日志区域
        bottom_frame = ttk.Frame(main_paned, padding=10)
        main_paned.add(bottom_frame, height=450)
        
        # 配置上半部分布局
        self.setup_top_section(top_frame)
        
        # 配置下半部分布局
        self.setup_bottom_section(bottom_frame)
        
    def setup_top_section(self, parent):
        """设置上半部分界面"""
        # 标题
        title_label = ttk.Label(parent, text="Socket客户端", font=("Arial", 16, "bold"))
        title_label.grid(row=0, column=0, columnspan=2, pady=(0, 15), sticky=tk.W)
        
        # 连接配置框架
        config_frame = ttk.LabelFrame(parent, text="连接配置", padding=10)
        config_frame.grid(row=1, column=0, sticky=tk.NSEW, padx=(0, 10), pady=(0, 15))
        
        ttk.Label(config_frame, text="服务器主机名:").grid(row=0, column=0, sticky=tk.W, pady=5)
        self.host_entry = ttk.Entry(config_frame, width=20)
        self.host_entry.insert(0, "localhost")
        self.host_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
        
        ttk.Label(config_frame, text="服务器端口:").grid(row=1, column=0, sticky=tk.W, pady=5)
        self.port_entry = ttk.Entry(config_frame, width=10)
        self.port_entry.insert(0, "12345")
        self.port_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W)
        
        # 按钮框架
        button_frame = ttk.Frame(config_frame)
        button_frame.grid(row=2, column=0, columnspan=2, pady=10)
        
        self.connect_button = ttk.Button(button_frame, text="连接服务器", command=self.connect_to_server, width=12)
        self.connect_button.pack(side=tk.LEFT, padx=5)
        
        self.disconnect_button = ttk.Button(button_frame, text="断开连接", command=self.disconnect_from_server, 
                                          state=tk.DISABLED, width=12)
        self.disconnect_button.pack(side=tk.LEFT, padx=5)
        
        self.test_button = ttk.Button(button_frame, text="测试连接", command=self.test_connection, width=12)
        self.test_button.pack(side=tk.LEFT, padx=5)
        
        # 状态显示
        status_frame = ttk.LabelFrame(parent, text="连接状态", padding=10)
        status_frame.grid(row=1, column=1, sticky=tk.NSEW, pady=(0, 15))
        
        self.status_label = ttk.Label(status_frame, text="未连接", foreground="red", font=("Arial", 10, "bold"))
        self.status_label.pack(anchor=tk.W, pady=2)
        
        self.debug_label = ttk.Label(status_frame, text="就绪", foreground="blue")
        self.debug_label.pack(anchor=tk.W, pady=2)
        
        # 消息发送框架 - 修复：添加发送按钮
        send_frame = ttk.LabelFrame(parent, text="发送消息到服务器", padding=10)
        send_frame.grid(row=2, column=0, columnspan=2, sticky=tk.EW, pady=(0, 15))
        
        # 输入框和发送按钮在同一行
        input_frame = ttk.Frame(send_frame)
        input_frame.pack(fill=tk.X, pady=5)
        
        ttk.Label(input_frame, text="消息:").pack(side=tk.LEFT, padx=(0, 5))
        self.message_entry = ttk.Entry(input_frame, font=("Arial", 11))
        self.message_entry.insert(0, "Hello, Server!")
        self.message_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
        self.message_entry.bind("<Return>", lambda event: self.send_message())
        
        self.send_button = ttk.Button(input_frame, text="发送", command=self.send_message, 
                                    state=tk.DISABLED, width=8)
        self.send_button.pack(side=tk.RIGHT)
        
        # 服务器消息框架
        server_frame = ttk.LabelFrame(parent, text="来自服务器的消息", padding=10)
        server_frame.grid(row=3, column=0, columnspan=2, sticky=tk.EW)
        
        self.server_msg_label = ttk.Label(server_frame, text="暂无消息", background="white", 
                                         relief=tk.SUNKEN, wraplength=600, padding=8)
        self.server_msg_label.pack(fill=tk.X, padx=5, pady=5)
        
        # 配置网格权重
        parent.columnconfigure(0, weight=1)
        parent.columnconfigure(1, weight=1)
        
    def setup_bottom_section(self, parent):
        """设置下半部分界面 - 专门的消息日志区域"""
        # 消息日志标题和操作按钮
        log_header = ttk.Frame(parent)
        log_header.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Label(log_header, text="消息日志", font=("Arial", 12, "bold")).pack(side=tk.LEFT)
        
        # 日志操作按钮
        btn_frame = ttk.Frame(log_header)
        btn_frame.pack(side=tk.RIGHT)
        
        ttk.Button(btn_frame, text="清空日志", command=self.clear_log, width=10).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="保存日志", command=self.save_log, width=10).pack(side=tk.LEFT, padx=5)
        
        # 巨大的消息日志显示区域
        log_container = ttk.Frame(parent)
        log_container.pack(fill=tk.BOTH, expand=True)
        
        # 创建文本框架和滚动条
        text_frame = ttk.Frame(log_container)
        text_frame.pack(fill=tk.BOTH, expand=True)
        
        # 垂直滚动条
        v_scrollbar = ttk.Scrollbar(text_frame)
        v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 水平滚动条
        h_scrollbar = ttk.Scrollbar(text_frame, orient=tk.HORIZONTAL)
        h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 超大的消息日志文本框
        self.log_text = tk.Text(text_frame, 
                               height=20,
                               width=80,
                               wrap=tk.WORD,
                               font=("Consolas", 10),
                               yscrollcommand=v_scrollbar.set,
                               xscrollcommand=h_scrollbar.set,
                               bg="white",
                               relief=tk.SUNKEN,
                               borderwidth=2)
        
        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.log_text.config(state=tk.DISABLED)
        
        # 配置滚动条
        v_scrollbar.config(command=self.log_text.yview)
        h_scrollbar.config(command=self.log_text.xview)
        
        # 底部状态栏
        bottom_status = ttk.Frame(parent)
        bottom_status.pack(fill=tk.X, pady=(10, 0))
        
        self.status_var = tk.StringVar(value="就绪")
        ttk.Label(bottom_status, textvariable=self.status_var, foreground="gray").pack(side=tk.LEFT)
        
        self.line_count_var = tk.StringVar(value="行数: 0")
        ttk.Label(bottom_status, textvariable=self.line_count_var, foreground="gray").pack(side=tk.RIGHT)
        
    # 其余方法保持不变...
    # [clear_log, save_log, log_message, update_status, update_line_count, 
    #  update_debug_info, test_connection, connect_to_server, disconnect_from_server,
    #  send_message, receive_messages, display_server_message, __del__ 等方法保持不变]
    
    def clear_log(self):
        """清空日志"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state=tk.DISABLED)
        self.update_status("日志已清空")
        self.update_line_count()
        
    def save_log(self):
        """保存日志到文件"""
        try:
            content = self.log_text.get(1.0, tk.END)
            if content.strip():
                filename = f"client_log_{time.strftime('%Y%m%d_%H%M%S')}.txt"
                with open(filename, 'w', encoding='utf-8') as f:
                    f.write(content)
                self.update_status(f"日志已保存到 {filename}")
            else:
                self.update_status("日志为空，无需保存")
        except Exception as e:
            self.update_status(f"保存失败: {str(e)}")
        
    def log_message(self, message):
        """在日志中添加消息"""
        self.log_text.config(state=tk.NORMAL)
        timestamp = time.strftime("%H:%M:%S")
        self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
        self.update_status(f"已记录: {message[:30]}..." if len(message) > 30 else f"已记录: {message}")
        self.update_line_count()
        
    def update_status(self, message):
        """更新状态栏"""
        self.status_var.set(message)
        
    def update_line_count(self):
        """更新行数显示"""
        line_count = self.log_text.get(1.0, tk.END).count('\n')
        self.line_count_var.set(f"行数: {line_count}")
        
    def update_debug_info(self, message):
        """更新调试信息"""
        self.debug_label.config(text=message)
        
    def test_connection(self):
        """测试服务器连接"""
        host = self.host_entry.get().strip()
        port_str = self.port_entry.get().strip()
        
        if not host:
            self.update_debug_info("错误: 请输入主机名")
            return
            
        if not port_str.isdigit():
            self.update_debug_info("错误: 端口号必须是数字")
            return
            
        port = int(port_str)
        self.update_status(f"正在测试连接 {host}:{port}...")
        
        try:
            test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            test_socket.settimeout(2)
            result = test_socket.connect_ex((host, port))
            test_socket.close()
            
            if result == 0:
                self.update_debug_info(f"成功: 端口 {port} 开放")
                self.log_message(f"连接测试: 端口 {port} 在 {host} 上开放")
                self.update_status("测试成功 - 端口开放")
            else:
                self.update_debug_info(f"失败: 端口 {port} 关闭")
                self.log_message(f"连接测试: 端口 {port} 在 {host} 上关闭")
                self.update_status("测试失败 - 端口关闭")
                
        except Exception as e:
            self.update_debug_info(f"测试错误: {str(e)}")
            self.log_message(f"连接测试错误: {str(e)}")
            self.update_status("测试错误")
        
    def connect_to_server(self):
        """连接到服务器"""
        if self.connected:
            self.log_message("已经连接到服务器")
            return
            
        try:
            host = self.host_entry.get().strip()
            port_str = self.port_entry.get().strip()
            
            if not host:
                messagebox.showerror("错误", "请输入有效的服务器主机名")
                return
                
            if not port_str.isdigit():
                messagebox.showerror("错误", "请输入有效的端口号")
                return
                
            port = int(port_str)
            
            self.update_debug_info(f"正在连接 {host}:{port}...")
            self.update_status(f"正在连接到 {host}:{port}...")
            self.log_message(f"尝试连接到 {host}:{port}")
            
            self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.client_socket.settimeout(5)
            self.client_socket.connect((host, port))
            
            self.connected = True
            self.connect_button.config(state=tk.DISABLED)
            self.disconnect_button.config(state=tk.NORMAL)
            self.send_button.config(state=tk.NORMAL)
            self.host_entry.config(state=tk.DISABLED)
            self.port_entry.config(state=tk.DISABLED)
            
            self.status_label.config(text=f"已连接到 {host}:{port}", foreground="green")
            self.update_debug_info("连接成功!")
            self.update_status("连接成功!")
            self.log_message(f"成功连接到服务器 {host}:{port}")
            
            receive_thread = threading.Thread(target=self.receive_messages, daemon=True)
            receive_thread.start()
            
        except socket.timeout:
            error_msg = "连接超时，请检查服务器是否运行"
            self.update_debug_info(error_msg)
            self.update_status("连接超时")
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)
        except ConnectionRefusedError:
            error_msg = "连接被拒绝，请检查服务器是否运行"
            self.update_debug_info(error_msg)
            self.update_status("连接被拒绝")
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)
        except Exception as e:
            error_msg = f"连接服务器失败: {str(e)}"
            self.update_debug_info(error_msg)
            self.update_status("连接失败")
            self.log_message(error_msg)
            messagebox.showerror("错误", error_msg)
            
    def disconnect_from_server(self):
        """断开与服务器的连接"""
        if not self.connected:
            return
            
        self.connected = False
        
        if self.client_socket:
            try:
                self.client_socket.close()
            except:
                pass
            self.client_socket = None
            
        self.connect_button.config(state=tk.NORMAL)
        self.disconnect_button.config(state=tk.DISABLED)
        self.send_button.config(state=tk.DISABLED)
        self.host_entry.config(state=tk.NORMAL)
        self.port_entry.config(state=tk.NORMAL)
        
        self.status_label.config(text="未连接", foreground="red")
        self.update_debug_info("已断开连接")
        self.update_status("已断开连接")
        self.log_message("已断开与服务器的连接")
        
    def send_message(self):
        """发送消息到服务器"""
        if not self.connected or not self.client_socket:
            messagebox.showerror("错误", "未连接到服务器")
            return
            
        message = self.message_entry.get().strip()
        if not message:
            messagebox.showwarning("警告", "消息不能为空")
            return
            
        try:
            self.client_socket.send(message.encode('utf-8'))
            self.log_message(f"发送消息: {message}")
            self.update_status(f"已发送: {message[:20]}..." if len(message) > 20 else f"已发送: {message}")
            self.message_entry.delete(0, tk.END)
            
        except Exception as e:
            error_msg = f"发送消息失败: {str(e)}"
            self.log_message(error_msg)
            self.update_status("发送失败")
            messagebox.showerror("错误", error_msg)
            self.disconnect_from_server()
            
    def receive_messages(self):
        """接收服务器消息"""
        while self.connected and self.client_socket:
            try:
                message = self.client_socket.recv(1024).decode('utf-8')
                if not message:
                    self.log_message("服务器关闭了连接")
                    break
                    
                self.root.after(0, lambda: self.display_server_message(message))
                self.root.after(0, lambda: self.log_message(f"收到服务器消息: {message}"))
                self.root.after(0, lambda: self.update_status(f"收到: {message[:20]}..." if len(message) > 20 else f"收到: {message}"))
                
            except socket.timeout:
                continue
            except ConnectionResetError:
                self.root.after(0, lambda: self.log_message("服务器连接已断开"))
                break
            except Exception as e:
                self.root.after(0, lambda: self.log_message(f"接收消息时出错: {str(e)}"))
                break
                
        self.root.after(0, self.disconnect_from_server)
        
    def display_server_message(self, message):
        """显示服务器消息"""
        self.server_msg_label.config(text=message)
        
    def __del__(self):
        """清理资源"""
        if self.client_socket:
            try:
                self.client_socket.close()
            except:
                pass

if __name__ == "__main__":
    root = tk.Tk()
    app = SocketClientApp(root)
    
    def on_closing():
        app.disconnect_from_server()
        root.destroy()
        
    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()